package com.dgj.mongo.dao;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.nutz.lang.Lang;

import com.dgj.mongo.EntityMapping;
import com.dgj.mongo.Pager;
import com.dgj.mongo.utils.Mongos;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public abstract class MongoDao<T> implements IMongoDao<T> {
	/**
	 * 数据映射接口
	 */
	protected EntityMapping<T> mapping;
	/**
	 * 具体的数据集合
	 */
	protected DBCollection collection;

	public MongoDao() {
		init();
	}

	@Override
	public boolean save(T t) {
		return collection.save(mapping.toDBObject(t)).getN() == 1;
	}

	@Override
	public int remove(T t) {
		return collection.remove(mapping.toDBObject(t)).getN();
	}

	@Override
	public int removeById(long id) {
		return collection.remove(new BasicDBObject().append("_id", id)).getN();
	}

	@Override
	public int removeByCnd(Object obj) {
		if (!(obj instanceof DBObject)) {
			obj = Mongos.obj2dbo(obj);
		}
		return collection.remove((DBObject) obj).getN();
	}

	@Override
	public boolean update(T t) {
		long id = 0;
		try {
			Field field = t.getClass().getDeclaredField("id");
			if (!field.isAccessible()) {
				field.setAccessible(true);
			}
			id = Long.parseLong(field.get(t).toString());
		} catch (Exception e) {
			e.printStackTrace();
			throw Lang.wrapThrow(e);
		}
		return updateById(id, t);
	}

	@Override
	public boolean updateById(long id, T t) {
		DBObject q = new BasicDBObject("_id", id);
		DBObject o = mapping.toDBObject(t);
		return collection.update(q, o).getN() == 1;
	}

	@Override
	public int updateByCnd(Object obj, T t) {
		if (!(obj instanceof DBObject)) {
			obj = Mongos.obj2dbo(obj);
		}
		return collection.update((DBObject) obj, mapping.toDBObject(t)).getN();
	}

	@Override
	public int updateFieldByCnd(Object obj, Object fieldDes) {
		DBObject q = null;
		if (obj instanceof DBObject) {
			q = (DBObject) obj;
		} else {
			q = Mongos.obj2dbo(obj);
		}
		DBObject o = null;
		if (fieldDes instanceof DBObject) {
			o = (DBObject) fieldDes;
		} else {
			o = Mongos.obj2dbo(fieldDes);
		}
		return collection.updateMulti(q, o).getN();
	}

	@Override
	public int updateFieldById(long id, Object fieldDes) {
		return updateFieldByCnd(new BasicDBObject("_id", id), fieldDes);
	}

	@Override
	public long count() {
		return collection.count();
	}

	@Override
	public long countByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		return collection.count(query);
	}

	@Override
	public List<T> listAll() {
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find();
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	@Override
	public List<T> findByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find(query);
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	@Override
	public T findOneByCnd(Object obj) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		DBObject object = collection.findOne(query);
		if (object == null) {
			return null;
		}
		return mapping.get(object);
	}

	@Override
	public T findById(long id) {
		DBObject q = new BasicDBObject("_id", id);
		DBObject object = collection.findOne(q);
		if (object == null) {
			return null;
		}
		return mapping.get(object);
	}

	@Override
	public List<T> listAllByPage(Pager pager) {
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find().skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	@Override
	public List<T> listAllByPage(Pager pager, DBObject orderBy) {
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find().sort(orderBy).skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	@Override
	public List<T> findByPage(Object obj, Pager pager) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find(query).skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	@Override
	public List<T> findByPage(Object obj, Pager pager, DBObject orderBy) {
		DBObject query = null;
		if (obj instanceof DBObject) {
			query = (DBObject) obj;
		} else {
			query = Mongos.obj2dbo(obj);
		}
		List<T> ts = new ArrayList<T>();
		DBCursor cursor = collection.find(query).sort(orderBy).skip(pager.start()).limit(pager.getPageSize());
		while (cursor.hasNext()) {
			ts.add(mapping.get(cursor.next()));
		}
		return ts;
	}

	/**
	 * 谨慎使用,设置之后将影响之后的全部操作
	 */
	public void setCollectionName(String collectionName) {
		this.collection = db.getCollection(collectionName);
	}

	/**
	 * 抽象的初始化方法，在此方法中进行实例对象属性的初始化<br>
	 * 子类如果不是抽象类必需实现此方法<br>
	 * 参考：<br>
	 * mapping = new EntityMapping<User>() { <br>
	 * <br>
	 * 
	 * @Override <br>
	 *           public User get(DBObject dbo) { <br>
	 *           return Lang.map2Object(Mongos.dbo2map(dbo), User.class); <br>
	 * <br>
	 * <br>
	 * @Override <br>
	 *           public DBCollection getCollection(DB db) { <br>
	 *           return
	 *           db.getCollection(User.class.getAnnotation(Collection.class
	 *           ).value()); <br>
	 * <br>
	 * <br>
	 * @Override <br>
	 *           public DBObject toDBObject(User t) { <br>
	 *           return Mongos.obj2dbo(t); <br>
	 * <br>
	 *           } ; <br>
	 *           collection = mapping.getCollection(db); <br>
	 */
	protected abstract void init();

}
